package emulator.shell.GUI.menu;

import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.util.Observable;
import java.util.Observer;

import javax.swing.DefaultCellEditor;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTable;
import javax.swing.JTextPane;
import javax.swing.table.TableColumn;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;

import emulator.hardware.io.Via6522;
import emulator.hardware.io.peripherals.iec.DeviceInfo;
import emulator.hardware.io.peripherals.iec.IecSim;
import emulator.hardware.io.peripherals.iec.IecSimDeviceRegistry;
import emulator.hardware.io.porthistory.PortStateHistory;
import emulator.hardware.nmos6502.Cpu6502;
import emulator.shell.GUI.graph.BitGraph;
import emulator.shell.GUI.viewmodel.DeviceTableModel;
import emulator.shell.GUI.windows.ManagedWindow;
import emulator.shell.GUI.windows.WindowManager;

public class WindowIEC extends GraphWindow implements ActionListener, Observer
{
	private int window_id;
	private WindowManager window_manager;
	private ManagedWindow window = null;
	private JTextPane content = null;
	private IecSim iec_sim = null;
	private JCheckBox log_state_button = null;
	private JButton clear_log_button = null;
	private JButton add_device_button = null;
	private JButton del_device_button = null;
	private JTable device_table = null;
	private DeviceTableModel device_table_model = null;
	private PortStateHistory portStateHistory1;
	private PortStateHistory portStateHistory2;
	private Via6522 via;
	
	public WindowIEC(WindowManager window_manager, int window_id, IecSim iec_sim, Via6522 via, Cpu6502 cpu, PortStateHistory portStateHistory1, PortStateHistory portStateHistory2)
	{
		this.window_manager = window_manager;
		this.window_id = window_id;
		this.iec_sim = iec_sim;
		this.via = via;
		this.cpu = cpu;
		this.portStateHistory1 = portStateHistory1;
		this.portStateHistory2 = portStateHistory2;
		iec_sim.getId();
	}

	
	@Override
	public void actionPerformed(ActionEvent arg0) {
		if (window_manager.isWindowActive(window_id))
			closeWindow();
		else
			openWindow();
	}

	private void openWindow()
	{
		window = window_manager.createWindow(window_id, "Debug IEC");
		addComponentsToPane(window.getContentPane());
		window.restorePosition();
		window.setVisible(true);
		fetchLogLines();
		iec_sim.addLogObserver(this);
		cpu.addRunningStateObserver(cpuObserver);
	}

	private void closeWindow()
	{
		cpu.deleteRunningStateObserver(cpuObserver);
		iec_sim.deleteLogObserver(this);
		window_manager.closeWindow(window_id);
	}

	private void addComponentsToPane(Container pane)
	{
        if (!(pane.getLayout() instanceof BorderLayout)) {
            pane.add(new JLabel("Container doesn't use BorderLayout!"));
            return;
        }

        JPanel device_panel = createDevicePanel();
		JPanel log_panel = createLogPanel();
	    JSplitPane splitPaneLeft = new JSplitPane(JSplitPane.VERTICAL_SPLIT, device_panel, log_panel);
	    splitPaneLeft.setOneTouchExpandable(true);
	    splitPaneLeft.setDividerLocation(150);

//		Provide minimum sizes for the two components in the split pane
	    Dimension minimumSize = new Dimension(100, 50);
	    device_panel.setMinimumSize(minimumSize);
	    log_panel.setMinimumSize(minimumSize);

	    if (portStateHistory1 != null && portStateHistory2 != null)
	    {
		    JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, splitPaneLeft, createGraphFrame());
		    splitPane.setOneTouchExpandable(true);
		    splitPane.setDividerLocation(splitPaneLeft.getPreferredSize().width);
		    
			pane.add(splitPane, BorderLayout.CENTER);
	    }
	    else
			pane.add(splitPaneLeft, BorderLayout.CENTER);
	}


	private JPanel createDevicePanel()
	{
		JPanel panel_config = new JPanel();
		panel_config.setLayout(new BorderLayout());

        JLabel label = new JLabel("Devices");
        panel_config.add(label, BorderLayout.PAGE_START);
		
        device_table = new JTable();
        device_table_model = new DeviceTableModel();
		device_table.setModel(device_table_model);
        for (int i = 0; i < device_table_model.getColumnCount(); i++) {
        	TableColumn column = device_table.getColumnModel().getColumn(i);
            column.setPreferredWidth(device_table_model.getColumnWidth(i));
        }

        TableColumn col = device_table.getColumnModel().getColumn(2);
        @SuppressWarnings("serial")
		class DeviceTypeCellEditor extends DefaultCellEditor { DeviceTypeCellEditor() { super(new JComboBox(DeviceInfo.getDeviceTypes()));} } 
        col.setCellEditor(new DeviceTypeCellEditor());
        
        device_table.setFillsViewportHeight(true);
        panel_config.add(new JScrollPane(device_table), BorderLayout.CENTER);
        
        add_device_button = new JButton("Add");
        add_device_button.addActionListener(new AddDevice());
        del_device_button = new JButton("Remove");
        del_device_button.addActionListener(new DelDevice());
        
		JPanel panel = new JPanel();
		panel.add(add_device_button, BorderLayout.WEST);
		panel.add(del_device_button, BorderLayout.EAST);
		panel_config.add(panel, BorderLayout.PAGE_END);
        
		return panel_config;
	}

	private JPanel createLogPanel()
	{
		JPanel panel_log = new JPanel();
		panel_log.setLayout(new BorderLayout());
        
        JLabel label = new JLabel("Debug Log");
        panel_log.add(label, BorderLayout.PAGE_START);
        
	    content = new JTextPane();
	    content.setEditable( false );
	    content.setPreferredSize( new Dimension( 300, 300 ) );
	    panel_log.add(new JScrollPane(content), BorderLayout.CENTER);
	    
		log_state_button = new JCheckBox("Log state");
		log_state_button.addItemListener(new ToggleLogState());
		log_state_button.setSelected(iec_sim.isStateLogEnabled());
		clear_log_button = new JButton("Clear log");
		clear_log_button.addActionListener(new ClearLog());
		
		JPanel panel = new JPanel();
		panel.add(log_state_button, BorderLayout.WEST);
		panel.add(clear_log_button, BorderLayout.EAST);
		panel_log.add(panel, BorderLayout.PAGE_END);
		return panel_log;
	}

	@Override
	protected BitGraph createGraph()
	{
		BitGraph bitGraph = new BitGraph(6);
		bitGraph.getGraphLine(0).setLabel("ATN").setStateHistory(portStateHistory1.getPortAout(7)).setPositionIndex(0);
		bitGraph.getGraphLine(1).setLabel("CLK/o").setStateHistory(portStateHistory2.getPortActrl(1)).setPositionIndex(1);
		bitGraph.getGraphLine(2).setLabel("DAT/o").setStateHistory(portStateHistory2.getPortBctrl(1)).setPositionIndex(3);
		bitGraph.getGraphLine(3).setLabel("CLK/i").setStateHistory(portStateHistory1.getPortAin(0)).setPositionIndex(2);
		bitGraph.getGraphLine(4).setLabel("DAT/i").setStateHistory(portStateHistory1.getPortAin(1)).setPositionIndex(4);
		bitGraph.getGraphLine(5).setLabel("SRQ").setStateHistory(portStateHistory2.getPortBctrl(0)).setPositionIndex(5);
		return bitGraph;
	}

	@Override
	protected void onCpuStoppped()
	{
		// NOP
	}


	@Override
	protected long getMaxTicks()
	{
		return via.getTicks();
	}
	
	private void fetchLogLines() {
		Document doc = content.getDocument();
		String log_line = iec_sim.getLogLine();
		while (log_line != null)
		{
		    try {
		    	doc.insertString(doc.getLength(), log_line, null);
		    } catch (BadLocationException e) {}
			log_line = iec_sim.getLogLine();
		}
	}
	
	@Override
	public void update(Observable arg0, Object arg1) {
        javax.swing.SwingUtilities.invokeLater(new LogWriteEvent());
	}

	public class LogWriteEvent implements Runnable {

		@Override
		public void run() {
			fetchLogLines();
		}
	}

	public class ClearLog implements ActionListener {

		@Override
		public void actionPerformed(ActionEvent event) {
			Document doc = content.getDocument();
			try {
				doc.remove(0, doc.getLength());
			} catch (BadLocationException e) { }
		}
	}

	public class ToggleLogState implements ItemListener {

		@Override
		public void itemStateChanged(ItemEvent event) {
			iec_sim.setStateLogEnabled(log_state_button.isSelected());
		}
	}

	public class AddDevice implements ActionListener {

		@Override
		public void actionPerformed(ActionEvent arg0) {
			Integer[] device_numbers = IecSimDeviceRegistry.instance().getAvailableDeviceNumbers();
			Integer device_number = (Integer)JOptionPane.showInputDialog(window, "Select a device number", "Add Device", JOptionPane.PLAIN_MESSAGE, null, device_numbers, device_numbers[0]);
			if (device_number != null)
			{
				DeviceInfo device = new DeviceInfo( device_number,
													device_number < 8 ? DeviceInfo.PRINTER : DeviceInfo.DISK,
													device_number < 8 ? "(null)" : ".");
				IecSimDeviceRegistry.instance().addDevice(device);
				device_table_model.addDevice(device);
			}

		}
	}

	public class DelDevice implements ActionListener {

		@Override
		public void actionPerformed(ActionEvent e) {
			int row = device_table.getSelectedRow();
			if (row >= 0)
			{
				DeviceInfo device = device_table_model.getDeviceForRow(row);
				if (device != null)
				{
					IecSimDeviceRegistry.instance().removeDevice(device);
					device_table_model.removeDevice(device);
				}
			}
		}
	}
}
